/*++

Copyright (C) Microsoft Corporation, All Rights Reserved

Module Name:

    WpdObjectProperties.cpp

Abstract:

    This module implements the WpdObjectProperties helper class
    used to store/retrieve Sensor properties/data
--*/

#include "internal.h"
#include "WpdObjectProperties.h"
#include "ipgeodll.h"

const PROPERTYKEY g_SupportedCommonProperties[] =
{
    WPD_OBJECT_ID,
    WPD_OBJECT_PERSISTENT_UNIQUE_ID,
    WPD_OBJECT_PARENT_ID,
    WPD_OBJECT_NAME,
    WPD_OBJECT_FORMAT,
    WPD_OBJECT_CONTENT_TYPE,
    WPD_OBJECT_CAN_DELETE,
};

const PROPERTYKEY g_SupportedDeviceProperties[] =
{
    WPD_DEVICE_FIRMWARE_VERSION,
    WPD_DEVICE_POWER_LEVEL,
    WPD_DEVICE_POWER_SOURCE,
    WPD_DEVICE_PROTOCOL,
    WPD_DEVICE_MODEL,
    WPD_DEVICE_SERIAL_NUMBER,
    WPD_DEVICE_SUPPORTS_NON_CONSUMABLE,
    WPD_DEVICE_MANUFACTURER,
    WPD_DEVICE_FRIENDLY_NAME,
    WPD_DEVICE_TYPE,
    WPD_FUNCTIONAL_OBJECT_CATEGORY,
};

const PROPERTYKEY g_SupportedGpsProperties[] =
{
    SENSOR_PROPERTY_TYPE,
    SENSOR_PROPERTY_STATE,
    SENSOR_PROPERTY_SAMPLING_RATE,
    SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
    SENSOR_PROPERTY_MANUFACTURER, 
    SENSOR_PROPERTY_MODEL,
    SENSOR_PROPERTY_SERIAL_NUMBER,
    SENSOR_PROPERTY_FRIENDLY_NAME,
    SENSOR_PROPERTY_DESCRIPTION, 
    SENSOR_PROPERTY_CONNECTION_TYPE,
    WPD_FUNCTIONAL_OBJECT_CATEGORY,
};

const PROPERTYKEY g_SupportedGpsDataFields[] =
{
    SENSOR_DATA_TYPE_TIMESTAMP,
    SENSOR_DATA_TYPE_LATITUDE_DEGREES,
    SENSOR_DATA_TYPE_LONGITUDE_DEGREES,
    SENSOR_DATA_TYPE_ALTITUDE_SEALEVEL_METERS,
    SENSOR_DATA_TYPE_ERROR_RADIUS_METERS,
};

/**
 *  This method is called to populate supported PROPERTYKEYs found on objects.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose supported property keys are being requested
 *  pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the specified object.
 */
HRESULT AddSupportedPropertyKeys(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;

    // Add Common PROPERTYKEYs for ALL WPD objects
    AddCommonPropertyKeys(pKeys);

    if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
    {
        // Add the PROPERTYKEYs for the 'DEVICE' object
        AddDevicePropertyKeys(pKeys);
    }

    if (strObjectID.CompareNoCase(SENSOR_GPS_ID) == 0)
    {
        // Add the PROPERTYKEYs for the file object
        AddGpsSensorPropertyKeys(pKeys);
    }

    return hr;
}

/**
 *  This method is called to populate supported PROPERTYKEYs found on data objects.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose supported property keys are being requested
 *  pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the specified object.
 */
HRESULT AddSupportedDataFieldKeys(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;

    if (strObjectID.CompareNoCase(SENSOR_GPS_ID) == 0)
    {
        // Add the PROPERTYKEYs for the file object
        AddGpsSensorDataFieldKeys(pKeys);
    }

    return hr;
}

/**
 *  This method is called to populate common PROPERTYKEYs found on ALL objects.
 *
 *  The parameters sent to us are:
 *  pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the ALL objects.
 */
VOID AddCommonPropertyKeys(
    IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++)
        {
            pKeys->Add(g_SupportedCommonProperties[dwIndex] );
        }
    }
}

/**
 *  This method is called to populate common PROPERTYKEYs found on the DEVICE object.
 *
 *  The parameters sent to us are:
 *  pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the DEVICE object.
 */
VOID AddDevicePropertyKeys(
    IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++)
        {
            pKeys->Add(g_SupportedDeviceProperties[dwIndex] );
        }
    }
}


/**
 *  This method is called to populate common PROPERTYKEYs found on GPS sensor objects.
 *
 *  The parameters sent to us are:
 *  pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the GPS sensor objects.
 */
VOID AddGpsSensorPropertyKeys(
    IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedGpsProperties); dwIndex++)
        {
            pKeys->Add(g_SupportedGpsProperties[dwIndex] );
        }
    }
}


VOID AddGpsSensorDataFieldKeys(IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedGpsDataFields); dwIndex++)
        {
            pKeys->Add(g_SupportedGpsDataFields[dwIndex] );
        }
    }
}


WpdObjectProperties::WpdObjectProperties() :
    m_gpsSamplingRate(100)
{

}

WpdObjectProperties::~WpdObjectProperties()
{

}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have
 *      been requested.
 *
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties.
 *      Since this parameter is optional, it may not exist.
 *      ! This driver currently ignores the filter parameter. !
 *
 *  The driver should:
 *  - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS
 */
HRESULT WpdObjectProperties::OnGetSupportedProperties(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection** ppKeys )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the supported property keys.
    hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) ppKeys);

    // Add supported property keys for the specified object to the collection
    if (hr == S_OK)
    {
        hr = AddSupportedPropertyKeys(wszObjectID, *ppKeys);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED
 *  command for the data child object of the wszObjectID.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have
 *      been requested.
 *
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties.
 *      Since this parameter is optional, it may not exist.
 *      ! This driver currently ignores the filter parameter. !
 *
 *  The driver should:
 *  - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS
 */
HRESULT WpdObjectProperties::OnGetSupportedDataFields(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection** ppKeys )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the supported property keys.
    hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) ppKeys);

    // Add supported property keys for the specified object to the collection
    if (hr == S_OK)
    {
        hr = AddSupportedDataFieldKeys(wszObjectID, *ppKeys);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which
 *      specific property values we are requested to return.
 *
 *  The driver should:
 *  - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetPropertyValues(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection* pKeys,
        IPortableDeviceValues** ppValues )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the property values.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppValues);

    // Read the specified properties on the specified object and add the property values to the collection.
    if (hr == S_OK)
    {
        hr = GetPropertyValuesForObject(wszObjectID, pKeys, *ppValues);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET
 *  command for the data child object of wszObjectID.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which
 *      specific property values we are requested to return.
 *
 *  The driver should:
 *  - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetDataFields(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection* pKeys,
        IPortableDeviceValues** ppValues )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the property values.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppValues);

    // Read the specified properties on the specified object and add the property values to the collection.
    if (hr == S_OK)
    {
        hr = GetDataValuesForObject(wszObjectID, pKeys, *ppValues);
    }

    return hr;
}

/*
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *
 *  The driver should:
 *  - Return all property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetAllPropertyValues(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT hr          = S_OK;
    LPWSTR  wszObjectID = NULL;
    CComPtr<IPortableDeviceValues>        pValues;
    CComPtr<IPortableDeviceKeyCollection> pKeys;

     //First get ALL parameters for this command.  If we cannot get ALL parameters
     //then E_INVALIDARG should be returned and no further processing should occur.

    /* Get the object identifier whose property values have been requested*/
    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
    }

    /* CoCreate a collection to store the property values.*/
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pValues);
    }

     //CoCreate a collection to store the property keys we are going to use
     //to request the property values of.
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) &pKeys);
    }

     //First we make a request for ALL supported property keys for the specified object.
     //Next, we delegate to our helper function GetPropertyValuesForObject( ) passing
     //the entire property key collection.  This will reuse existing implementation
     //in our driver to perform the GetAllPropertyValues operation.
    if (hr == S_OK)
    {
        hr = AddSupportedPropertyKeys(wszObjectID, pKeys);
        if (hr == S_OK)
        {
            hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues);
        }
    }

     //S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ).
     //S_FALSE means that 1 or more property values could not be retrieved successfully.
     //The value for the specified property key should be set to the error HRESULT of
     //the reason why the property could not be read.
     //(i.e. an error of HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) if a property value was
     // requested and is not supported by the specified object.)
    if (SUCCEEDED(hr))
    {
        // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results
        HRESULT hrTemp = S_OK;
        hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues);

        if(FAILED(hrTemp))
        {
            hr = hrTemp;
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(wszObjectID);

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which
 *      specific property values we are requested to write.
 *
 *  The driver should:
 *  - Write all requested property values.  For each property, a write result should be returned in the
 *      write result property store.
 *  - If any property write failed, the corresponding write result value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were written successfully.
 *  - S_FALSE should be returned if any property write failed.
 *  - Any error return indicates that the driver did not write any results, and the caller will
 *      not attempt to unpack any property write results.
 */
HRESULT WpdObjectProperties::OnSetPropertyValues(
        __in LPWSTR  wszObjectID,
        IPortableDeviceValues* pValues,
        IPortableDeviceValues** ppResults
        )
{
    HRESULT hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD   cValues     = 0;
    BOOL fHasError = FALSE;

    // CoCreate a collection to store the property set operation results.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppResults);

    // Set the property values on the specified object
    if (hr == S_OK)
    {
        hr = pValues->GetCount(&cValues);

        if (hr == S_OK)
        {
            for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                PROPVARIANT var;

                PropVariantInit( &var );

                hr = pValues->GetAt(dwIndex, &Key, &var);

                if (hr == S_OK)
                {
                    //Check for settable properties
                    if( IsEqualPropertyKey( Key, SENSOR_PROPERTY_SAMPLING_RATE ) )
                    {
                        if( var.vt == VT_UI4 )
                        {
                            if (strObjectID.CompareNoCase(SENSOR_GPS_ID) == 0)
                            {
                                m_gpsSamplingRate = var.ulVal;
                            }
                            else
                            {
                                hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG);
                                fHasError = TRUE;
                            }
                        }
                        else if( var.vt == VT_EMPTY )
                        {//Do not allow delete the property
                            hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED);
                            fHasError = TRUE;
                        }
                        else
                        {
                            hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG);
                            fHasError = TRUE;
                        }
                    }
                    else
                    {
                        hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED);
                        fHasError = TRUE;
                    }
                }

                if( FAILED( hr ) )
                {
                    break;
                }
            }
        }

        // Since we have set failures for the property set operations we must let the application
        // know by returning S_FALSE. This will instruct the application to look at the
        // property set operation results for failure values.
        if( (hr == S_OK) && fHasError )
        {
            hr = S_FALSE;
        }
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property attributes we want to return.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys containing a single value,
 *      which is the key identifying the specific property attributes we are requested to return.
 *
 *  The driver should:
 *  - Return the requested property attributes.  If any property attributes failed to be retrieved,
 *    the corresponding value should be set to type VT_ERROR with the 'scode' member holding the
 *    HRESULT reason for the failure.
 *  - S_OK should be returned if all property attributes were read successfully.
 *  - S_FALSE should be returned if any property attribute failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetPropertyAttributes(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT     hr          = S_OK;
    LPWSTR      wszObjectID = NULL;
    PROPERTYKEY Key         = WPD_PROPERTY_NULL;
    CComPtr<IPortableDeviceValues>  pAttributes;

    // First get ALL parameters for this command.  If we cannot get ALL parameters
    // then E_INVALIDARG should be returned and no further processing should occur.

    // Get the object identifier whose property attributes have been requested
    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
    }

    // Get the list of property keys whose attributes are being requested
    if (hr == S_OK)
    {
        hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &Key);
    }

    // CoCreate a collection to store the property attributes.
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pAttributes);
    }

    // Get the attributes for the specified properties on the specified object and add them
    // to the collection.
    if (hr == S_OK)
    {
        hr = GetPropertyAttributesForObject(wszObjectID, Key, pAttributes);
    }

    if (SUCCEEDED(hr))
    {
        // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES value in the results
        HRESULT hrTemp = S_OK;
        hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributes);

        if(FAILED(hrTemp))
        {
            hr = hrTemp;
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(wszObjectID);

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_DELETE
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose properties should be deleted.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys indicating which
 *      properties to delete.
 *
 *  The driver should:
 *  - Delete the specified properties from the object.
 *  - S_OK should be returned if all specified properties were successfully deleted.
 *  - E_ACCESSDENIED should be returned if the client attempts to delete a property which is not deletable (i.e.
 *    WPD_PROPERTY_ATTRIBUTE_CAN_DELETE is FALSE for that property.)
 */
HRESULT WpdObjectProperties::OnDeleteProperties(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT hr = E_ACCESSDENIED;

    UNREFERENCED_PARAMETER(pParams);
    UNREFERENCED_PARAMETER(pResults);

    // This driver has no properties which can be deleted.

    return hr;
}

/**
 *  This method is called to populate property values for the object specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose properties are being requested.
 *  pKeys - the list of property keys of the properties to request from the object
 *  pValues - an IPortableDeviceValues which will contain the property values retreived from the object
 *
 *  The driver should:
 *  Read the specified properties for the specified object and populate pValues with the
 *  results.
 */
HRESULT WpdObjectProperties::GetPropertyValuesForObject(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys,
    IPortableDeviceValues*         pValues)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD       cKeys       = 0;

    if ((wszObjectID == NULL) ||
        (pKeys       == NULL) ||
        (pValues     == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    hr = pKeys->GetCount(&cKeys);

    if (hr == S_OK)
    {
        // Get values for the DEVICE object
        if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
        {
            for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                hr = pKeys->GetAt(dwIndex, &Key);

                if (hr == S_OK)
                {
                    // Preset the property value to 'error not supported'.  The actual value
                    // will replace this value, if read from the device.
                    pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));

                    // Set DEVICE object properties
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_FIRMWARE_VERSION))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, DEVICE_FIRMWARE_VERSION_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_LEVEL))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, DEVICE_POWER_LEVEL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_SOURCE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, WPD_POWER_SOURCE_EXTERNAL);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_PROTOCOL))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_PROTOCOL, DEVICE_PROTOCOL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_MODEL))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_MODEL, DEVICE_MODEL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_SERIAL_NUMBER))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_SUPPORTS_NON_CONSUMABLE))
                    {
                        hr = pValues->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, DEVICE_SUPPORTS_NONCONSUMABLE_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_MANUFACTURER))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, DEVICE_MANUFACTURER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_FRIENDLY_NAME))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, DEVICE_FRIENDLY_NAME_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_TYPE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, WPD_DEVICE_TYPE_GENERIC);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_ID, WPD_DEVICE_OBJECT_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_NAME, WPD_DEVICE_OBJECT_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, WPD_DEVICE_OBJECT_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, L"");
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
                    {
                        hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
                    {
                        hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_DEVICE);
                    }
                }
            }
        }
        else if (strObjectID.CompareNoCase(SENSOR_GPS_ID) == 0)
        {
            for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                hr = pKeys->GetAt(dwIndex, &Key);

                if (hr == S_OK)
                {
                    // Preset the property value to 'error not supported'.  The actual value
                    // will replace this value, if read from the device.
                    pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));

                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_TYPE))
                    {
                        hr = pValues->SetGuidValue(SENSOR_PROPERTY_TYPE, SENSOR_TYPE_LOCATION_GPS);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_STATE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_STATE, 0);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_SAMPLING_RATE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_SAMPLING_RATE, m_gpsSamplingRate);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID))
                    {
                        // This should not be hardcoded but must be CoCreated at Runtime the first time the driver is installed
                        // and subsequently read from the device property store
                        hr = pValues->SetGuidValue(SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID, SENSOR_GPS_GUID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_MANUFACTURER))
                    {
                        hr = pValues->SetStringValue(SENSOR_PROPERTY_MANUFACTURER, DEVICE_MANUFACTURER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_MODEL))
                    {
                        hr = pValues->SetStringValue(SENSOR_PROPERTY_MODEL, DEVICE_MODEL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_SERIAL_NUMBER))
                    {
                        hr = pValues->SetStringValue(SENSOR_PROPERTY_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_FRIENDLY_NAME))
                    {
                        hr = pValues->SetStringValue(SENSOR_PROPERTY_FRIENDLY_NAME, SENSOR_GPS_FRIENDLY_NAME);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_DESCRIPTION))
                    {
                        hr = pValues->SetStringValue(SENSOR_PROPERTY_DESCRIPTION, SENSOR_GPS_FRIENDLY_NAME);
                    }
                    else
                    if (IsEqualPropertyKey(Key, SENSOR_PROPERTY_CONNECTION_TYPE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(SENSOR_PROPERTY_CONNECTION_TYPE, 0);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_ID, SENSOR_GPS_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_NAME))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_NAME, SENSOR_GPS_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_PERSISTENT_UNIQUE_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_PERSISTENT_UNIQUE_ID, SENSOR_GPS_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_PARENT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_PARENT_ID, WPD_DEVICE_OBJECT_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_FORMAT))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_FORMAT, WPD_OBJECT_FORMAT_UNSPECIFIED);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_CONTENT_TYPE))
                    {
                        hr = pValues->SetGuidValue(WPD_OBJECT_CONTENT_TYPE, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_CAN_DELETE))
                    {
                        hr = pValues->SetBoolValue(WPD_OBJECT_CAN_DELETE, FALSE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
                    {
                        hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, SENSOR_CATEGORY_LOCATION);
                    }
                }
            }
        }
    }

    return hr;
}

HRESULT
_GetLatOrLong(
    __in            BOOL fGetLat,
    __out           float *pfltValue)
{
    HRESULT hr = 0;
    PFN_IPGEO_GETLATITUDE pfnGetLat = NULL;
    PFN_IPGEO_GETLONGITUDE pfnGetLong = NULL;
    HMODULE hIpGeo = NULL;
    BOOL fFoundValue = FALSE;

    __try
    {
        if (NULL == (hIpGeo = LoadLibrary(L"ipgeodll.dll")))
        {
            hr = (HRESULT) GetLastError();
            __leave;
        }

        if (NULL == (pfnGetLat = (PFN_IPGEO_GETLATITUDE) GetProcAddress(
            hIpGeo, "IPGeoGetLatitude")))
        {
            hr = (HRESULT) GetLastError();
            __leave;
        }

        if (NULL == (pfnGetLong = (PFN_IPGEO_GETLONGITUDE) GetProcAddress(
            hIpGeo, "IPGeoGetLongitude")))
        {
            hr = (HRESULT) GetLastError();
            __leave;
        }

        if (TRUE == fGetLat)
            hr = (HRESULT) pfnGetLat(pfltValue, &fFoundValue);
        else
            hr = (HRESULT) pfnGetLong(pfltValue, &fFoundValue);

        if (FALSE == fFoundValue)
            hr = E_FAIL;
    }
    __finally
    {
        if (NULL != hIpGeo)
            FreeLibrary(hIpGeo);
    }

    return hr;
}

/**
 *  This method is called to populate data values for the object specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose properties are being requested.
 *  pKeys - the list of property keys of the properties to request from the object
 *  pValues - an IPortableDeviceValues which will contain the property values retreived from the object
 *
 *  The driver should:
 *  Read the specified properties for the specified object and populate pValues with the
 *  results.
 */
HRESULT WpdObjectProperties::GetDataValuesForObject(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys,
    IPortableDeviceValues*         pValues)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD       cKeys       = 0;
    float       fltValue    = 0;

    if ((wszObjectID == NULL) ||
        (pKeys       == NULL) ||
        (pValues     == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    hr = pKeys->GetCount(&cKeys);

    if (hr == S_OK)
    {
        if (strObjectID.CompareNoCase(SENSOR_GPS_ID) == 0)
        {
            for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                hr = pKeys->GetAt(dwIndex, &Key);

                if (hr == S_OK)
                {
                    PROPVARIANT var;
                    PropVariantInit( &var );

                    // Preset the property value to 'error not supported'.  The actual value
                    // will replace this value, if read from the device.
                    pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));
    
                    if (IsEqualPropertyKey(Key, SENSOR_DATA_TYPE_TIMESTAMP))
                    {                        
                        //Get the current time in  SYSTEMTIME format
                        SYSTEMTIME st;
                        ::GetSystemTime(&st);

                        // Convert the SYSTEMTIME into FILETIME
                        FILETIME ft;
                        if(FALSE == ::SystemTimeToFileTime(&st, &ft))
                        {
                            hr = HRESULT_FROM_WIN32(GetLastError());
                        }

                        if(S_OK == hr)
                        {
                            var.vt                      = VT_FILETIME;
                            var.filetime.dwLowDateTime  = ft.dwLowDateTime;
                            var.filetime.dwHighDateTime = ft.dwHighDateTime;

                            hr = pValues->SetValue(SENSOR_DATA_TYPE_TIMESTAMP, &var);
                        }
                       
                    }

                    if (IsEqualPropertyKey(Key, SENSOR_DATA_TYPE_LATITUDE_DEGREES))
                    {
                        if (SUCCEEDED(hr = _GetLatOrLong(TRUE, &fltValue)))
                        {
                            var.vt      = VT_R8;
                            var.dblVal  = (DOUBLE) fltValue;
                            hr = pValues->SetValue(SENSOR_DATA_TYPE_LATITUDE_DEGREES, &var);
                        }
                    }

                    if (IsEqualPropertyKey(Key, SENSOR_DATA_TYPE_LONGITUDE_DEGREES))
                    {
                        if (SUCCEEDED(hr = _GetLatOrLong(FALSE, &fltValue)))
                        {
                            var.vt      = VT_R8;
                            var.dblVal  = (DOUBLE) fltValue;
                            hr = pValues->SetValue(SENSOR_DATA_TYPE_LONGITUDE_DEGREES, &var);
                        }
                    }

                    if (IsEqualPropertyKey(Key, SENSOR_DATA_TYPE_ALTITUDE_SEALEVEL_METERS))
                    {
                        hr = pValues->SetFloatValue(SENSOR_DATA_TYPE_ALTITUDE_SEALEVEL_METERS, 132.0F);
                    }

                    if (IsEqualPropertyKey(Key, SENSOR_DATA_TYPE_ERROR_RADIUS_METERS))
                    {
                        var.vt      = VT_R8;
                        var.dblVal  = 10.0L;

                        hr = pValues->SetValue(SENSOR_DATA_TYPE_ERROR_RADIUS_METERS, &var);
                    }

                    // Free any allocated resources
                    PropVariantClear( &var );
                }
            }
        }
    }

    return hr;
}

/**
 *  This method is called to populate property attributes for the object and property specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose property attributes are being requested.
 *  Key - the property whose attributes are being requested
 *  pAttributes - an IPortableDeviceValues which will contain the resulting property attributes
 *
 *  The driver should:
 *  Read the property attributes for the specified property on the specified object and
 *  populate pAttributes with the results.
 */
HRESULT WpdObjectProperties::GetPropertyAttributesForObject(
    LPCWSTR                wszObjectID,
    const PROPERTYKEY&     Key,
    IPortableDeviceValues* pAttributes)
{
    HRESULT hr = S_OK;

    if ((wszObjectID == NULL) ||
        (pAttributes == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    UNREFERENCED_PARAMETER(wszObjectID);
    UNREFERENCED_PARAMETER(Key);

    //
    // Since ALL of our properties have the same attributes, we are ignoring the
    // passed in wszObjectID and Key parameters.  These parameters allow you to
    // customize attributes for properties on specific objects. (i.e. WPD_OBJECT_ORIGINAL_FILE_NAME
    // may be READ/WRITE on some objects and READONLY on others. )
    //

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED);
    }

    return hr;
}


